﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using EzyThirdLogic;

namespace EzyThirdGUI
{
    public partial class Form1 : Form
    {
        #region Private Fields

        private BindingList<FunctionalDependency> _listOfFDs; // list of FDs entered by user
        private string[] steps = new string[8];
        private int currentStep;
        private FunctionalDependencySet fdSet;
        private Partitioner partitioner;
        private FunctionalDependencySetCollection fdSetCollection;
        private Merger merger;
        private string[][] arrayJ;
        private TDRemover transitivity;
        private FunctionalDependencySetCollection fdSetCollection2;

        #endregion

        #region Constructor

        public Form1()
        {
            InitializeComponent();

            // initialize list of functional dependencies
            _listOfFDs = new BindingList<FunctionalDependency>();
            _listOfFDs.Add(new FunctionalDependency("A", "B"));
            _listOfFDs.Add(new FunctionalDependency("A", "C"));
            _listOfFDs.Add(new FunctionalDependency("B", "C"));
            _listOfFDs.Add(new FunctionalDependency("B", "D"));
            _listOfFDs.Add(new FunctionalDependency("D", "B"));
            _listOfFDs.Add(new FunctionalDependency("A,B,E", "F"));
            
            // set the auto-generate column property of the datagridview to false
            dgvFDs.AutoGenerateColumns = false;

            // set data source for the datagridview
            dgvFDs.DataSource = _listOfFDs;
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Generate relations from the given functional dependencies
        /// </summary>
        private void btnGenerateRelations_Click(object sender, EventArgs e)
        {
            // initialize
            Cleanup();

            // get the input set of functional dependencies
            try
            {
                fdSet = new FunctionalDependencySet(_listOfFDs);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occurred while parsing input.\nError Details: " + ex.Message);
            }

            // input validation
            try
            {
                if (!ValidateFunctionalDependencies(fdSet))
                {
                    return;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occurred while validating input.\nError Details: " + ex.Message);
            }

            // update bit determinant and bit dependent
            try
            {
                foreach (var fd in fdSet.FDSet)
                {
                    fd.UpdateBitFromString();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occurred while processing input.\nError Details: " + ex.Message);
            }

            // disable the functional dependency data grid view and generate relation button
            dgvFDs.Enabled = false;
            btnGenerateRelations.Enabled = false;

            // step 0:
            try
            {
                steps[0] = fdSet.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occured in step 0!\nError Details: " + ex.Message);
            }

            // step 1:
            try
            {
                fdSet = ExtraAttributesRemover.RemoveExtraneousAttributes(fdSet);
                steps[1] = fdSet.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occured in step 1!\nError Details: " + ex.Message);
            }

            // step 2
            try
            {
                fdSet = CoverFinding.RemoveRedundantFunctionalDependencies(fdSet);
                steps[2] = fdSet.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occured in step 2!\nError Details: " + ex.Message);
            }

            // step 3
            try
            {
                partitioner = new Partitioner();
                fdSetCollection = partitioner.Partition(fdSet);
                steps[3] = fdSetCollection.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occured in step 3!\nError Details: " + ex.Message);
            }

            // step 4
            try
            {
                merger = new Merger();
                fdSetCollection = merger.Merge(fdSetCollection, fdSet);
                arrayJ = merger.GetArrayJ();
                steps[4] = "J = {";
                var mergerJ = merger.GetJ();
                for (int i = 0; i < mergerJ.Count; i++)
                {
                    steps[4] += mergerJ[i].ToString2();
                    if (i != mergerJ.Count - 1)
                    {
                        steps[4] += ",";
                    }
                }
                steps[4] += "}\n\n";
                steps[4] += fdSetCollection.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occured in step 4!\nError Details: " + ex.Message);
            }

            // step 5
            try
            {
                var data = fdSetCollection.FDSetCollectionToArray(fdSetCollection.CountFunctionalDependency());
                transitivity = new TDRemover(data);
                data = transitivity.PlotTransitivityStep5();
                transitivity.IdentifyTransitivityStep5(data, arrayJ);
                steps[5] = transitivity.GetTDVarStep5();
                steps[5] += transitivity.ToStringStep5();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occured in step 5!\nError Details: " + ex.Message);
            }

            // step 6
            try
            {
                var listFDSet = transitivity.GetList();
                RelationsBuilder constructor = new RelationsBuilder(listFDSet);
                steps[6] = constructor.ToStringStep6();
                fdSetCollection2 = constructor.DisplayRelationStep6();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occured in step 6!\nError Details: " + ex.Message);
            }

            // step 7
            try
            {
                var linkedList = new LinkedList<string>();
                linkedList = SuperfluousRemover.RemoveSuperfluous(fdSetCollection2).FDSetCollectionToRelation();
                steps[7] = SuperfluousRemover.Superfluous_tostring(linkedList);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error has occured in step 7!\nError Details: " + ex.Message);
            }

            // set the output to the content of the last step (step 7)
            tbxOutput.Enabled = true;
            lbStep.Text = "Step 0: Original Set of Functional Dependencies";
            tbxOutput.Text = steps[0];
            currentStep = 0;
            btnBackward.Enabled = false;
            btnForward.Enabled = true;

            // enable the functional dependency data grid view and generate relation button
            dgvFDs.Enabled = true;
            btnGenerateRelations.Enabled = true;
        }

        private void Cleanup()
        {
            // clear output
            tbxOutput.Clear();

            // disable next and previous buttons
            btnForward.Enabled = false;
            btnBackward.Enabled = false;

            // initialize steps
            for (int i = 0; i < steps.Length; i++)
            {
                steps[i] = string.Empty;
            }

            // initialize current step
            currentStep = 0;
        }

        private bool ValidateFunctionalDependencies(FunctionalDependencySet fdSet)
        {
            if (fdSet.FDSet.Count == 0)
            {
                ShowEmptyFDSetErrorMessage();
                return false;
            }

            foreach (var fd in fdSet.FDSet)
            {
                if (fd.DeterminantIsEmpty())
                {
                    ShowEmptyDeterminantErrorMessage();
                    return false;
                }

                if (fd.DependentIsEmpty())
                {
                    ShowEmptyDependentErrorMessage();
                    return false;
                }

                if (fd.DeterminantContainsInvalidAttributes())
                {
                    ShowInvalidDeterminantFormatErrorMessage();
                    return false;
                }

                if (fd.DependentContainsInvalidAttributes())
                {
                    ShowInvalidDependentFormatErrorMessage();
                    return false;
                }

                if (fd.IsTrivialFunctionalDependency())
                {
                    ShowTrivialFDMessage();
                    return false;
                }
            }

            return true;
        }

        private void ShowTrivialFDMessage()
        {
            MessageBox.Show("Input contains trivial functional dependencies! Please remove them.");
        }

        private void ShowEmptyFDSetErrorMessage()
        {
            MessageBox.Show("Can not generate relations from the empty set of functional dependencies!");
        }

        private void ShowEmptyDeterminantErrorMessage()
        {
            MessageBox.Show("Determinant can not be empty!");
        }

        private void ShowEmptyDependentErrorMessage()
        {
            MessageBox.Show("Dependent can not be empty!");
        }

        private void ShowInvalidDeterminantFormatErrorMessage()
        {
            MessageBox.Show("Invalid determinant format! Please use only upper case letters.\n" +
                            "Example: A,B,C,D");
        }

        private void ShowInvalidDependentFormatErrorMessage()
        {
            MessageBox.Show("Invalid dependent format! Please use only upper case letters.\n" +
                            "Example: A,B,C,D");
        }

        /// <summary>
        /// Remove the row when the link "Remove"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgvFDs_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            // check if the column is the "Remove" column
            if (e.ColumnIndex != 2)
            {
                return;
            }

            // get row index
            int rowIndex = e.RowIndex;

            // remove the functional dependency from the binding list
            _listOfFDs.RemoveAt(rowIndex);

            // reset binding
            _listOfFDs.ResetBindings();
        }

        #endregion

        private void btnBackward_Click(object sender, EventArgs e)
        {
            if (currentStep == 0)
            {
                return;
            }

            // decrease the current step number
            currentStep--;

            // set the step lable
            lbStep.Text = GetStepLabel(currentStep);

            // set the output content of the step
            tbxOutput.Text = steps[currentStep];

            // adjust backward/forward
            AdjustBackwardForward(currentStep);
        }

        private string GetStepLabel(int stepNumber)
        {
            switch (stepNumber)
            {
                case 0:
                    return "Step 0: Original Set of Functional Dependencies";
                case 1:
                    return "Step 1: Eliminate Extraneous Attributes";
                case 2:
                    return "Step 2: Find Covering";
                case 3:
                    return "Step 3: Partition";
                case 4:
                    return "Step 4: Merge Equivalent Keys";
                case 5:
                    return "Step 5: Eliminate Transitive Dependencies";
                case 6:
                    return "Step 6: Construct Relations";
                case 7:
                    return "Step 7: Eliminate Superfluous Attributes";
                default:
                    return "Unknown Step";
            }
        }

        private void btnForward_Click(object sender, EventArgs e)
        {
            if (currentStep == 7)
            {
                return;
            }

            // decrease the current step number
            currentStep++;

            // set the step lable
            lbStep.Text = GetStepLabel(currentStep);

            // set the output content of the step
            tbxOutput.Text = steps[currentStep];

            // adjust backward/forward
            AdjustBackwardForward(currentStep);
        }

        private void AdjustBackwardForward(int currentStep)
        {
            switch (currentStep)
            {
                case 0:
                    btnBackward.Enabled = false;
                    btnForward.Enabled = true;
                    break;
                case 1:
                    btnBackward.Enabled = true;
                    btnForward.Enabled = true;
                    break;
                case 2: 
                case 3:
                case 4:
                case 5:
                case 6:
                    btnBackward.Enabled = true;
                    btnForward.Enabled = true;
                    break;
                case 7:
                    btnBackward.Enabled = true;
                    btnForward.Enabled = false;
                    break;
                default:
                    break;
            }
        }

        private void btnClearAll_Click(object sender, EventArgs e)
        {
            // initialize if null
            if (_listOfFDs == null)
                _listOfFDs = new BindingList<FunctionalDependency>();

            // clear input
            _listOfFDs.Clear();
        }

        private void btnAddFD_Click(object sender, EventArgs e)
        {
            // add new row
            _listOfFDs.Add(new FunctionalDependency());

            // reset databinding
            _listOfFDs.ResetBindings();
        }
    }
}
